home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BCI NET
/
BCI NET Dec 94.iso
/
archives
/
applications
/
wp
/
ged11.lha
/
Install
/
data
/
main
/
GoldED
/
API
/
spell
/
main.c
next >
Wrap
C/C++ Source or Header
|
1994-08-12
|
22KB
|
729 lines
/* -----------------------------------------------------------------------------
GoldED API client example code, ©1994 Dietmar Eilert. Dice:
dcc main.c -// -proto -mi -r -2.0 -l reqtoolss.lib -o ram:spell
ENGLISH
Note: Compiling this code requires reqtools includes & reqtools linker
libraries. The ReqTools library is © Nico François.
The following code adds spell checking capabilities to GoldED. It is based on
the ISpell package and the ISpell dictionary (available on Fish disks). ISpell
has to be installed properly (within a valid command path) before you can use
this API client. This example uses synchronous ARexx communication: Requests
are PutMsg()'ed to GoldED's port, followed by a WaitPort() to get the editor's
response. This works fine since we need no ARexx communication after the API
link has been established. If there were ARexx communication with GoldED AFTER
the link has been established (i.e. after sending the 'API PORT=...' command to
register with GoldED), we would have to use an asynchronous design beeing
capable of answering incoming API messages while waiting for completion of
ARexx requests sent to GoldED.
Note: La compilation de ce code requiert les includes et les bibliothèques de
liaison reqtools. La bibliothèque ReqTools est © Nico François.
FRENCH
Le code suivant ajoute des fonctions de correction orthographique à GoldED. Il
est basé sur le pack Ispell et sur le dictionnaire Ispell (disponible sur
disquettes Fish). ISpell doit être installé proprement (à l'intérieur d'un
chemin de commande valide) avant que vous ne puissiez utiliser le client API.
Cet exemple utilise la communication ARexx synchrone: les requêtes sont
PutMsg()'ées sur le port de GoldED, suivies d'un WaitPort() pour avoir la
réponse de l'éditeur. Ceci fonctionne très bien comme nous n'avons pas besoin
de communications ARexx après l'établissement de la liaison API (c-à-d après
l'envoi de la commande 'API PORT=...' pour s'enregistrer à GoldED), nous
aurions dû utiliser un système asynchrone étant capable de répondre aux
messages API arrivants, tout en attendant la fin des requêtes ARexx envoyées à
GoldED.
-------------------------------------------------------------------------------
*/
/// "includes"
#define Prototype extern
#include <exec/exec.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/rdargs.h>
#include <intuition/intuition.h>
#include <utility/tagitem.h>
#include <libraries/gadtools.h>
#include <rexx/errors.h>
#include <rexx/rxslib.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/rexxsyslib_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/reqtools_protos.h>
#include <clib/alib_protos.h>
#include <devices/audio.h>
#include "golded:api/include/golded.h"
Prototype void main(int, char **);
Prototype BOOL InitISpell(void);
Prototype void HandleAPI(BOOL, char *);
Prototype ULONG *SendRexxCommand(char *, char *, struct MsgPort *, char *);
Prototype void CheckWord(struct APIMessage *, struct MsgPort *, BOOL);
Prototype void Beep(void);
Prototype UWORD ComputeX(struct TextFont *, UWORD);
Prototype UWORD ComputeY(struct TextFont *, UWORD);
Prototype char *ShowSpell(char *, struct List *);
Prototype struct Node *SearchNode(struct List *, UWORD);
struct Library *ReqToolsBase;
///
/// "main"
void
main(int argc, char **argv)
{
const char *version = "$VER: spell 1.3 (" __COMMODORE_DATE__ ")";
if ((argc == 2) || (argc == 3)) {
if (ReqToolsBase = (struct ReqToolsBase *)OpenLibrary("reqtools.library", 0)) {
char *host; // name of GoldED's port
BOOL ask; // ask user if misspelled word detected ?
ask = (argc == 3); // the easy way ;-)
host = argv[--argc] + 5; // host name (HOST=...) always passed as last argument
if (InitISpell()) // launch ISpell
HandleAPI(ask, host); // register with GoldED
CloseLibrary(ReqToolsBase);
}
}
else
puts("syntax error: SPELL ASK/S HOST/K");
exit(0);
}
///
/// "init"
/* -------------------------------- IntiISpell ---------------------------------
Prepare ISpell. Return TRUE if successful.
*/
BOOL
InitISpell()
{
if (FindPort("IRexxSpell"))
return(TRUE);
else {
struct TagItem tags[] = {SYS_Output, NULL, SYS_Input, NULL, SYS_Asynch, TRUE, TAG_DONE};
if (SystemTagList("ispell -r", tags) != -1) {
UWORD n;
for (n = 0; n < 100; n++, Delay(10))
if (FindPort("IRexxSpell"))
return(TRUE);
}
}
puts("ISpell not available");
return(FALSE);
}
///
/// "api management"
/* --------------------------------- HandleAPI ---------------------------------
Register with GoldED & handle incoming API messages.
*/
void
HandleAPI(ask, host)
BOOL ask;
char *host;
{
struct MsgPort *rexxPort, *apiPort;
if (apiPort = CreateMsgPort()) {
if (rexxPort = CreateMsgPort()) {
char command[255];
ULONG *result;
sprintf(command, "API PORT=%ld MASK=%ld", apiPort, API_CLASS_ROOT | API_CLASS_KEY);
if (result = SendRexxCommand(host, command, rexxPort, NULL)) {
if (*result == RC_OK) {
BOOL active = TRUE;
do {
struct APIMessage *apiMsg, *nextMsg;
while (!(apiMsg = (struct APIMessage *)GetMsg(apiPort)))
WaitPort(apiPort);
do {
for (nextMsg = apiMsg; nextMsg; nextMsg = nextMsg->api_Next) {
if (nextMsg->api_State == API_STATE_NOTIFY) {
switch (nextMsg->api_Class) {
case API_CLASS_ROOT:
switch (nextMsg->api_Action) {
case API_ACTION_DIE:
active = FALSE;
break;
case API_ACTION_INTRODUCE:
static struct TagItem tags[] = {
API_Client_Name, "spell",
API_Client_Copyright, "©1994 Dietmar Eilert",
API_Client_Purpose, "Adds online spell checking",
TAG_DONE
};
nextMsg->api_Data = tags;
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
break;
case API_CLASS_KEY:
switch (nextMsg->api_Action) {
case API_ACTION_VANILLAKEY:
// checks are performed after white space characters:
if ((UBYTE)nextMsg->api_Data < 'A')
CheckWord(nextMsg, rexxPort, ask);
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
}
}
ReplyMsg((struct Message *)apiMsg);
} while (apiMsg = (struct APIMessage *)GetMsg(apiPort));
} while (active);
}
}
SendRexxCommand("IRexxSpell", "EXIT", rexxPort, NULL);
DeleteMsgPort(rexxPort);
}
DeleteMsgPort(apiPort);
}
}
///
/// "check word"
/* --------------------------------- CheckWord ---------------------------------
Check word. This function is called BEFORE GoldED actually inserts a white
space character (space, colon, ...) at the current cursor position. We assume
the current position to be a white space character to guarantee correct
behavior if the user inserts a character into a word. If ISpell complains about
a word we will either issue a beep (ask = FALSE) or additionally display a list
of suggestions (<ask> = TRUE).
Excerpt taken from man/ispell.1: If the word is not in the dictionary, but
there are near misses, then the result line contains an '&', a space, and a
list of the near misses separated by spaces.
Français
Vérifie le mot. Cette fonction est appelée AVANT que GoldED n'insère un espace
caractère blanc (espace, colonne, ...) à la position actuelle du curseur. Nous
supposons que la position actuelle doit être un caractère espace blanc pour
garantir un comportement normal si l'utilisateur insère un caractère dans un
mot. Si ISpell se plaint d'un mot, nous émettrons soit un bip (ask=FALSE) ou
afficher une liste de suggestions (en option) (<ask> = TRUE).
Exception tirée de man/ispell.1: si le mot n'est pas dans le dictionnaire, mais
qu'il y a des mots très ressemblants, alors la ligne résultat contient un '&',
un espace, et une liste des mots les plus ressemblants séparés par des espaces.
*/
void
CheckWord(apiMsg, rexxPort, ask)
struct APIMessage *apiMsg;
struct MsgPort *rexxPort;
BOOL ask;
{
struct EditConfig *config = apiMsg->api_Config;
UWORD column;
UBYTE *buffer, *next;
column = config->Column;
buffer = config->Current;
// cursor placed directly after word ?
if (column && (buffer[--column] > '@')) {
static char word[1000], result[1000];
UWORD wordLen;
// find beginning of word
for (next = buffer + column, wordLen = 1; column && (*(next - 1) > '@'); --column, --next)
++wordLen;
movmem(next, word, wordLen);
word[wordLen] = 0;
strins(word, "QUICKCHECK ");
// make ISpell check word
if (SendRexxCommand("IRexxSpell", word, rexxPort, result)) {
if (stricmp(result, "ok")) {
Beep();
// send additional CHECK command to get further information
if (ask && SendRexxCommand("IRexxSpell", word + 5, rexxPort, result)) {
if (*result == '&') {
// translate ISpell result string into exec list
struct List list;
struct Node *node, *nextNode;
char *start, *end, *selection;
for (NewList(&list), end = result + 2; *end;) {
for (start = end; *end && (*end != 32); ++end);
if (node = (struct Node *)AllocVec(sizeof(struct Node), MEMF_ANY | MEMF_CLEAR)) {
node->ln_Name = start;
AddTail(&list, node);
}
if (*end)
*end++ = 0;
}
// insert user selection into text
if (selection = ShowSpell(apiMsg->api_Screen, &list)) {
UWORD newLen = strlen(selection);
if (newLen != wordLen) {
movmem(next + wordLen, next + newLen, config->CurrentLen - (next - buffer));
config->CurrentLen += (newLen - wordLen);
config->Column += (newLen - wordLen);
while (config->Column > config->CurrentLen)
buffer[(config->CurrentLen)++] = ' ';
}
movmem(selection, next, newLen);
// make GoldED redisplay the current line
apiMsg->api_Refresh |= API_REFRESH_LINE;
}
for (node = list.lh_Head; nextNode = node->ln_Succ; node = nextNode)
FreeVec(node);
}
}
}
}
}
}
///
/// "gui"
/* --------------------------------- ShowSpell ---------------------------------
Show ISpell suggestions. Return user selection or NULL.
Afficher les suggestions de ISpell. Retourne la sélection de l'utilisateur ou NULL.
*/
char *
ShowSpell(screen, list)
char *screen;
struct List *list;
{
char *result = NULL;
struct Screen *scr;
if (scr = LockPubScreen(screen)) {
struct TextFont *font;
if (font = OpenDiskFont(scr->Font)) {
APTR visualInfo;
if (visualInfo = GetVisualInfoA(scr, NULL )) {
struct Gadget *context, *glist, *gad;
if (context = CreateContext(&glist)) {
UWORD ww, wh, offX, offY;
LONG displayWidth, displayHeight;
struct Window *win;
offX = scr->WBorLeft;
offY = scr->RastPort.TxHeight + scr->WBorTop + 1;
struct NewGadget newGad = {
offX,
offY,
ComputeX(font, 221),
ComputeY(font, 156),
NULL, scr->Font, 0, 0, visualInfo, 0
};
gad = CreateGadget(LISTVIEW_KIND, context, &newGad, GTLV_ShowSelected, NULL, GTLV_Labels, list, GTLV_Selected, 0, TAG_DONE);
rtGetVScreenSize(scr, &displayWidth, &displayHeight);
ww = ComputeX(font, 221);
wh = ComputeY(font, 156);
if (win = OpenWindowTags( NULL,
WA_Left, ((displayWidth - ww)>>1) - scr->ViewPort.DxOffset,
WA_Top, ((displayHeight - wh)>>1) - scr->ViewPort.DyOffset,
WA_Width, ww + offX + scr->WBorRight,
WA_Height, wh + offY + scr->WBorBottom,
WA_IDCMP, LISTVIEWIDCMP | IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_RAWKEY,
WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE,
WA_Gadgets, glist,
WA_Title, "ISpell",
WA_PubScreen, scr,
TAG_DONE
)) {
ULONG class;
UWORD active;
active = 0;
result = list->lh_Head->ln_Name;
struct IntuiMessage *msg;
struct Node *node;
GT_RefreshWindow(win, NULL);
do {
while (!(msg = GT_GetIMsg(win->UserPort)))
WaitPort(win->UserPort);
switch (class = msg->Class) {
case RAWKEY:
WORD step = (msg->Code == CURSORUP) ? -1 : 1;
if (node = SearchNode(list, active + step)) {
active += step;
result = node->ln_Name;
GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, active, GTLV_MakeVisible, active, TAG_DONE);
}
break;
case IDCMP_VANILLAKEY:
if (msg->Code == 27)
class = CLOSEWINDOW;
if (msg->Code == 13)
class = IDCMP_GADGETUP;
break;
case IDCMP_GADGETUP:
result = SearchNode(list, active = msg->Code)->ln_Name;
break;
}
GT_ReplyIMsg(msg);
} while ((class & (GADGETUP | CLOSEWINDOW)) == NULL);
if (class == CLOSEWINDOW)
result = NULL;
CloseWindow(win);
}
}
FreeVisualInfo(visualInfo);
}
}
UnlockPubScreen(screen, scr);
}
return(result);
}
/* --------------------------------- ComputeX ----------------------------------
Resize element of width <value> according to current font
Redimensionner l'élément de largeur <valeur> selon la police actuelle
*/
UWORD
ComputeX(font, value)
UWORD value;
struct TextFont *font;
{
return((font->tf_XSize * value) / 8);
}
/* --------------------------------- ComputeY ----------------------------------
Resize element of height <value> according to current font
Redimensionner l'élément de hauteur <valeur> selon la police actuelle
*/
UWORD
ComputeY(font, value)
UWORD value;
struct TextFont *font;
{
return((font->tf_YSize * value) / 8);
}
///
/// "misc"
/* ---------------------------------- SearchNode --------------------------------
Return pointer to node if ordinal number (0, ...) is known.
Retourne le pointeur sur la node si un nombre ordinal (0, ...) est connu.
*/
struct Node *
SearchNode(list, ordinal)
struct List *list;
UWORD ordinal;
{
struct Node *node;
for (node = list->lh_Head; node->ln_Succ; --ordinal, node = node->ln_Succ)
if (!ordinal)
return(node);
return(NULL);
}
/* ----------------------------------- Beep ------------------------------------
Short audible beep
Bip sonore bref
*/
void
Beep()
{
struct IOAudio *audioIO;
if (audioIO = (struct IOAudio *)AllocVec(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) {
struct MsgPort *audioMP;
if (audioMP = CreateMsgPort()) {
UBYTE whichannel[] = { 1, 2, 4, 8 };
audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
audioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
audioIO->ioa_Request.io_Command = ADCMD_ALLOCATE;
audioIO->ioa_Request.io_Flags = ADIOF_NOWAIT;
audioIO->ioa_AllocKey = 0;
audioIO->ioa_Data = whichannel;
audioIO->ioa_Length = sizeof(whichannel);
if (!OpenDevice("audio.device", 0, (struct IORequest *)audioIO, 0)) {
__chip const static UBYTE waveptr[2] = {127, -127};
audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
audioIO->ioa_Request.io_Command = CMD_WRITE;
audioIO->ioa_Request.io_Flags = ADIOF_PERVOL;
audioIO->ioa_Data = waveptr;
audioIO->ioa_Length = 2;
audioIO->ioa_Period = 1015;
audioIO->ioa_Volume = 32;
audioIO->ioa_Cycles = 60; // 44;
BeginIO((struct IORequest *)audioIO );
WaitPort(audioMP);
GetMsg (audioMP);
CloseDevice((struct IORequest *)audioIO);
}
DeleteMsgPort(audioMP);
}
FreeVec(audioIO);
}
}
///
/// "arexx"
/* ---------------------------------- SendRexxCommand -------------------------
Send ARexx message & wait for answer. Return pointer to result or NULL.
Envoyer un message ARexx & attendre une réponse. Retourne un pointeur sur le
résultat ou NULL.
*/
ULONG *
SendRexxCommand(port, cmd, replyPort, buffer)
char *cmd, *port, *buffer;
struct MsgPort *replyPort;
{
struct MsgPort *rexxport;
Forbid();
if (rexxport = FindPort(port)) {
struct RexxMsg *rexxMsg, *answer;
if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {
if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {
static ULONG result;
rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;
PutMsg(rexxport, &rexxMsg->rm_Node);
do {
WaitPort(replyPort);
if (answer = (struct RexxMsg *)GetMsg(replyPort))
result = answer->rm_Result1;
} while (!answer);
Permit();
if (answer->rm_Result1 == RC_OK) {
if (answer->rm_Result2) {
if (buffer)
strcpy(buffer, (char *)answer->rm_Result2);
DeleteArgstring((char *)answer->rm_Result2);
}
}
DeleteArgstring((char *)ARG0(answer));
DeleteRexxMsg(answer);
return(&result);
}
}
}
Permit();
return(NULL);
}
///